package com.litidea.commons.utils;

import static com.litidea.commons.dict.StaticDics.DOWNLOAD_FILE_CONTENT;

import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.charset.Charset;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;

import com.litidea.dataobjects.FileWapper;

/**
 * 文件处理工具
 * @author Administrator
 *
 */
public class FileUtils {
	
	private static Log logger = LogFactory.getLog(FileUtils.class);
	
	private final static int COPY_FILE_SIZE = 1024 * 1024;//每次拷贝1M文件内容
	
	/**
	 * 通过基本路径分配一个子目录
	 * @param baseUrl
	 * @param userId
	 * @return
	 */
	public static String mkdir(String baseUrl , Integer userId) {
		String tempPath = baseUrl + userId + "_" + System.currentTimeMillis();
		File file = new File(tempPath);
		while(file.exists()) {
			tempPath = baseUrl + userId + "_" + System.currentTimeMillis();
			file = new File(tempPath);
		}
		boolean isCreate = file.mkdir();
		if(isCreate) return tempPath;
		return null;
	}
	
	/**
	 * 通过基本路径分配一个子目录
	 * @param baseUrl
	 * @param userId
	 * @return
	 */
	public static boolean mkdir(String path) {
		File file = new File(path);
		if(file.exists()) return false;//如果目录存在，不做任何操作（用于反复生成文件时使用）
		return file.mkdir();
	}
	
	
	/**
	 * 压缩文件列表
	 * @param zipFileName
	 * @param files
	 * @throws IOException 
	 */
	public static void zipFiles(String zipFileName , boolean deleteFiles , FileWapper ...files) throws IOException {
		ZipOutputStream output = null;
		try {
			output = new ZipOutputStream(new FileOutputStream(zipFileName));
			for(FileWapper file : files) {
				zipOneFile(output , deleteFiles, file);
			}
		}finally {
			FileUtils.closeStream(output);
		}
	}
	
	/**
	 * 压缩一个文件
	 * @param output
	 * @param file
	 * @throws IOException 
	 */
	public static void zipOneFile(ZipOutputStream output , 
			boolean deleteFile, FileWapper file) throws IOException {
		FileInputStream inputStream = null;
		try {
			ZipEntry ze = new ZipEntry(file.getRealName());//文件真名
			output.putNextEntry(ze);
			inputStream = new FileInputStream(file.getFile());
			int fileAvailable = inputStream.available();
			copyInputStreamToOutputStream(inputStream, output,
					fileAvailable);
			if(deleteFile) {
				closeStream(inputStream);
				file.getFile().delete();
			}
		}finally {
			closeStream(inputStream);
		}
	}
	
	/**
	 * 拷贝文件
	 * @param srcFileName
	 * @param srcFileName
	 */
	public static void copyFile(File srcFile , File dstFile , 
			boolean isDeleteSrc) throws Exception {
		if(!srcFile.exists()) throw new RuntimeException("src file not exists!");
		if(dstFile.exists()) throw new RuntimeException("dst file exists!");
		FileInputStream inputStream = null;
		FileOutputStream outputStream = null;
		try {
			inputStream = new FileInputStream(srcFile);
			outputStream = new FileOutputStream(dstFile);
			copyInputStreamToOutputStream(inputStream, outputStream,
					inputStream.available());
			if(isDeleteSrc) {
				closeStream(inputStream);
				srcFile.delete();
			}
		}catch(Exception e) {
			closeStream(outputStream);
			deleteFile(dstFile);//抛错时删除目标文件
			logger.error("when copy file error" , e);
			throw e;
		}finally {
			closeStream(inputStream , outputStream);
		}
	}

	/**
	 * 将输入流内容拷贝到输出流
	 * @param inputStream
	 * @param outputStream
	 * @param fileAvailable
	 * @throws IOException
	 */
	private static void copyInputStreamToOutputStream(
			FileInputStream inputStream, OutputStream outputStream,
			int fileAvailable) throws IOException {
		if(fileAvailable <= COPY_FILE_SIZE) {
			byte []bytes = new byte[fileAvailable];
			inputStream.read(bytes);
			outputStream.write(bytes);
			outputStream.flush();
		}else {
			byte []bytes = new byte[COPY_FILE_SIZE];
			int len = inputStream.read(bytes);
			while(len > 0) {
				outputStream.write(bytes , 0 , len);
				outputStream.flush();
				len = inputStream.read(bytes);
			}
		}
	}
	
	/**
	 * 拷贝文件
	 * @param srcFileName
	 * @param srcFileName
	 */
	public static void copyFile(String srcFileName , String dstFileName , 
			boolean isDeleteSrc) throws Exception {
		copyFile(new File(srcFileName) , new File(dstFileName) , isDeleteSrc);
	}
	
	/**
	 * 拷贝文件
	 * @param srcFileName
	 * @param srcFileName
	 */
	public static void copyFile(File srcFile , String dstFileName , 
			boolean isDeleteSrc) throws Exception {
		copyFile(srcFile , new File(dstFileName) , isDeleteSrc);
	}
	
	/**
	 * 输出文件信息到客户端
	 * @param response
	 * @param file
	 * @param download
	 * @return
	 * @throws IOException 
	 */
	public static String responseFile(HttpServletResponse response , 
			String filePath , String outputFileName, String download) throws IOException {
		File file = new File(filePath);
		if(!file.exists()) throw new RuntimeException("file not exists:" + filePath);
		FileInputStream inputStream = null;
		OutputStream outputStream = null;
		try {
			String name = file.getName();
			prepareResponseHeader(response, outputFileName, download, name);
			//开始输出文件
			outputStream = response.getOutputStream();
			inputStream = new FileInputStream(file);
			int fileAvailable = inputStream.available();
			copyInputStreamToOutputStream(inputStream , outputStream , fileAvailable);
			return null;
		}finally {
			closeStream(inputStream);
		}
	}

	/**
	 * 处理输出内容的头部
	 * @param response
	 * @param outputFileName
	 * @param download
	 * @param name
	 * @throws UnsupportedEncodingException
	 */
	private static void prepareResponseHeader(HttpServletResponse response,
			String outputFileName, String download, String name)
			throws UnsupportedEncodingException {
		String defaultContentType = DOWNLOAD_FILE_CONTENT.get("*");
		if("true".equals(download)) {
			response.setContentType(defaultContentType);
			response.setHeader("Content-disposition", "attachment; filename=\"" + new String(outputFileName.getBytes("gbk") , "ISO-8859-1") + "\"");
		}else {
			response.setHeader("Content-disposition", "inline; filename=\"" + new String(outputFileName.getBytes("gbk") , "ISO-8859-1") + "\"");
			int lastIndex = name.lastIndexOf(".");
			if(lastIndex == -1) {
				response.setContentType(defaultContentType);
			}else {
				String end = name.substring(lastIndex).toLowerCase();
				String contentType = DOWNLOAD_FILE_CONTENT.get(end);
				if(contentType == null) {
					response.setContentType(defaultContentType);
				}else {
					response.setContentType(contentType);
				}
			}
		}
	}
	
	/**
	 * 删除文件
	 * @param file
	 */
	public static boolean deleteFile(File file) {
		if(file != null && file.exists()) {
			try {
				return file.delete();
			}catch(Exception e) {
				logger.error("when delete file error" , e);
			}
		}
		return false;
	}
	
	/**
	 * 删除文件或目录，重载
	 * @param file
	 * @return
	 */
	public static boolean deleteFileOrDirectory(File file) {
		if(!file.exists()) return false;
		if(file.isFile()) return deleteFile(file);
		if(file.isDirectory()) {
			File []childFiles = file.listFiles();
			for(File childFile : childFiles) {
				deleteFileOrDirectory(childFile);
			}
			return file.delete();
		}
		return false;
	}
	
	/**
	 * 删除文件或目录
	 * @param filePath
	 */
	public static boolean deleteFileOrDirectory(String filePath) {
		if(filePath == null) return false;
		File file = new File(filePath);
		return deleteFileOrDirectory(file);
	}
	
	/**
	 * 重命名文件
	 * @param filePath1
	 * @param filePath2
	 * @return
	 */
	public static boolean renameFile(String filePath1 , String filePath2) {
		return renameFile(new File(filePath1) , new File(filePath2));
	}
	
	/**
	 * 重命名文件或文件夹
	 * @param srcFilePath
	 * @param dstFilePath
	 * @return
	 */
	public static boolean renameFile(String baseUri,String srcFileName , String dstFileName) {
		return renameFile(new File(baseUri + srcFileName) , new File(baseUri + dstFileName));
	}
	
	/**
	 * 对比连个文件随机的一段内容
	 * @param file1
	 * @param file2
	 * @return
	 */
	public static boolean compareFiles(String fileName1 , String fileName2) throws FileNotFoundException,IOException {
		return compareFiles(new File(fileName1) , new File(fileName2));
	}
	
	/**
	 * 对比连个文件随机的一段内容
	 * @param file1
	 * @param file2
	 * @return
	 */
	public static boolean compareFiles(File file1 , File file2) throws FileNotFoundException,IOException {
		RandomAccessFile radomFile1 = null;
		RandomAccessFile radomFile2 = null;
		try {
			radomFile1 = new RandomAccessFile(file1 , "r");
			radomFile2 = new RandomAccessFile(file2 , "r");
			
			if(radomFile1.length() == radomFile2.length()) {
				return compareFileBytes(radomFile1, radomFile2);
			}else {
				//System.out.println(radomFile1.length() + "\t" + radomFile2.length() + "\t" + Math.abs(radomFile1.length() - radomFile2.length()));
				if(Math.abs(radomFile1.length() - radomFile2.length()) <= 300) {
					return compareFileBytes(radomFile1, radomFile2);
				}
			}
			return false;
		}finally {
			closeStream(radomFile1 , radomFile2);
		}
	}

	/**
	 * 对比连个文件的字节码
	 * @param radomFile1
	 * @param radomFile2
	 * @return
	 * @throws IOException
	 */
	private static boolean compareFileBytes(RandomAccessFile radomFile1,
			RandomAccessFile radomFile2) throws IOException {
		long length = radomFile1.length();
		byte[]bytes1 = null , bytes2 = null;
		if(length > COPY_FILE_SIZE) {
			long radomV = (long)(Math.random() * (length - COPY_FILE_SIZE));
			radomFile1.seek(radomV);
			radomFile2.seek(radomV);
			bytes1 = new byte[COPY_FILE_SIZE];
			bytes2 = new byte[COPY_FILE_SIZE];
		}else {//小于1M
			bytes1 = new byte[(int)length];
			bytes2 = new byte[(int)length];
			
		}
		radomFile1.read(bytes1);
		radomFile2.read(bytes2);
		int notSameBytes = 0 , notSameMax = bytes1.length / 10;//如果百分之10不一样，则认为不一样
		for(int i = 0 , size = bytes1.length ; i < size ; i++) {
			if(bytes1[i] != bytes2[i]) {
				notSameBytes++;
				if(notSameBytes >= notSameMax) return false;
			}
		}
		return true;
	}
	
	/**
	 * 文件重命名
	 * @param file
	 * @return
	 */
	public static boolean renameFile(File file , File dstFile) {
		if(dstFile.exists()) {//目标文件存在，不允许覆盖
			return false;
		}
		if(file.exists()) {
			return file.renameTo(dstFile);
		}
		return false;
	}
	
	/**
	 * 输入流关闭
	 * @param inputStream
	 */
	public static void closeStream(Closeable ...streams) {
		if(streams == null) return;
		for(Closeable stream : streams) {
			if(stream != null) {
				try {
					stream.close();
				}catch(Exception e) {
					logger.error("when close stream error" , e);
				}
			}
		}
	}
	
	/**
	 * 将output的内容刷掉缓冲区内容
	 * @param output
	 */
	public static void flushOutput(OutputStream output) {
		if(output != null) {
			try {
				output.flush();
			}catch(Exception e) {
				logger.error("when flush output error" , e);
			}
		}
	}
	
	/**
	 * 将文件读入成String
	 * @param path
	 * @return
	 */
	public static String readFile(String path) {
		try {
			StringBuffer buf = new StringBuffer();
			BufferedReader breader = null;
			breader = new BufferedReader(new InputStreamReader(
					new FileInputStream((path)), Charset.forName("utf-8")));
			while (breader.ready())
				buf.append((char) breader.read());
			breader.close();
			return buf.toString();
		} catch (Exception e) {
			return null;
		}
	}
	
	/**
	 * 拷贝文件
	 * @param srcFileName
	 * @param dstFileName
	 * @throws IOException
	 */
	public static void copyFileByMappedByteBuffer(String srcFileName , String dstFileName) throws IOException {
		FileChannel inFileChannel = new RandomAccessFile(srcFileName , "r").getChannel();
		FileChannel outFileChannel = new RandomAccessFile(dstFileName , "rw").getChannel();
		try {
			long fileSize = inFileChannel.size();
			long position = 0;
			while(position < fileSize) {
				long copyFileSize = Math.min((fileSize - position), COPY_FILE_SIZE);
				MappedByteBuffer mappedByteBuffer = outFileChannel.map(MapMode.READ_WRITE,  position ,  copyFileSize);
				inFileChannel.read(mappedByteBuffer);
				position += mappedByteBuffer.position();
			}
		}finally {
			closeStream(outFileChannel);
		}
	}

}
